package services

import (
	"context"
	"encoding/json"
	"gbox-one-machine/conf"
	"gbox-one-machine/dao"
	"gbox-one-machine/log"
	"gbox-one-machine/models"
	"gbox-one-machine/rpc"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"net/http"
	"sync"
	"syscall"
	"time"
)

//methods
const (
	MethodBreathingLamp = "breathing-lamp"
)

var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

// OneMachine defination
type OneMachineService struct {
	conf       *conf.Config
	dao        *dao.Dao
	rpc        rpc.IRPC
	ch         chan struct{}
	execute    bool
	serialPort bool
	sync.RWMutex
}

// NewOneMachineService create OneMachineService
func NewOneMachineService(cf *conf.Config, d *dao.Dao) *OneMachineService {
	return &OneMachineService{
		conf:       cf,
		dao:        d,
		ch:         make(chan struct{}),
		execute:    true,
		serialPort: false,
	}
}

func (s *OneMachineService) getMasterService() rpc.IRPC {
	s.RLock()
	defer s.Unlock()

	return s.rpc
}

func (s *OneMachineService) setMasterService(r rpc.IRPC) {
	s.Lock()
	defer s.Unlock()

	s.rpc = r
}

func (s *OneMachineService) setExecute(isExecute bool) {
	s.Lock()
	defer s.Unlock()
	s.execute = isExecute
}

func (s *OneMachineService) setSerialPort(isOpen bool) {
	s.Lock()
	defer s.Unlock()
	s.serialPort = isOpen
}

// Bind OneMachine Method
func (s *OneMachineService) Bind(c *gin.Context) {
	//fmt.Println(111)
	//if s.getMasterService() != nil {
	//	c.JSON(http.StatusOK, &models.HTTPResp{
	//		ReturnCode:   models.RespCodeError,
	//		ErrorMessage: "node allready bind",
	//	})
	//	return
	//}

	ws, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		c.JSON(http.StatusInternalServerError, &models.HTTPResp{
			ReturnCode:   models.RespCodeError,
			ErrorMessage: err.Error(),
		})
		return
	}

	_, cancel := context.WithCancel(conf.GetCtx())
	r, err := rpc.NewJSONRPC(&rpc.JSONRPCOpts{
		Ws: ws,
		CloseHandler: func() error {
			s.setMasterService(nil)
			cancel()
			log.Logger.Infof("Master %s closed", ws.RemoteAddr())
			return nil
		},
	})
	if err != nil {
		c.JSON(http.StatusInternalServerError, &models.HTTPResp{
			ReturnCode:   models.RespCodeError,
			ErrorMessage: err.Error(),
		})
		return
	}

	s.setMasterService(r)
	if err := s.rpc.Register(MethodBreathingLamp, s.BreathingLamp); err != nil {
		log.Logger.Errorf("Regist method %s err: %s", MethodBreathingLamp, err)
	}

}

func (s *OneMachineService) BreathingLamp(ctx context.Context, request interface{}) (interface{}, error) {
	if s.execute {
		s.setExecute(false)
		go func() {
			reqJSON, ok := request.([]byte)
			if !ok {
				//return nil, fmt.Errorf("BreathingLamp Request invalid %v", request)
				log.Logger.Errorf("BreathingLamp Request invalid %v", request)
			}
			req := new(models.BreathingLamp)
			if err := json.Unmarshal(reqJSON, req); err != nil {
				//return nil, fmt.Errorf("BreathingLamp Request err: %s", err)
				log.Logger.Errorf("BreathingLamp Request err: %s", err)
			}

			lib := syscall.NewLazyDLL("./machine_dll.dll")
			var ret uintptr
			var err error
			if !s.serialPort {
				open := lib.NewProc("serial_open")
				ret, _, err = open.Call()
				if err != nil {
					log.Logger.Infof("serial_open result is:%s", ret)
				}
				s.setSerialPort(true)
			}
			openorder := lib.NewProc("serial_openorder")
			ret, _, err = openorder.Call()
			if err != nil {
				log.Logger.Infof("serial_openorder result is:%s", ret)
			}
			defer func() {
				closeorder := lib.NewProc("serial_closeorder")
				ret, _, err = closeorder.Call()
				if err != nil {
					log.Logger.Infof("serial_closeorder result is:%s", ret)
				}
			}()
			time.Sleep(time.Duration(req.Frequency) * time.Second)
			s.setExecute(true)
		}()
		return &rpc.Response{}, nil
	} else {
		return &rpc.Response{}, nil
	}

}
